﻿using System;
using System.Net;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Windows.Forms;
using System.Data;


namespace MOSSLoadTest
{
    public class PageError
    {
        private bool processed;
        private string errorCode;
        private string htmlCode;
        private string url;

        private bool IsProcessed
        {
            get { return processed; }
            set { processed = value; }
        }

        public PageError(string ErrorCode, string BadUrl, string HtmlCode)
        {
            errorCode = ErrorCode;
            htmlCode = HtmlCode;
            url = BadUrl;
            processed = false;
        }

        public string ErrorCode
        {
            get { return errorCode; }
        }

        public string HtmlCode
        {
            get { return htmlCode; }
        }

        public string Url
        {
            get { return url; }
        }
    }


    public class Crawler
    {
        private static long count = 0;
        private static long errorCount = 0;
        private static bool abort = false;
        private static object sync = new object();

        public static bool Abort
        {
            get { return Crawler.abort; }
            set { Crawler.abort = value; }
        }

        public static long ErrorCount
        {
            get {
                lock (sync)
                {

                    return errorCount;
                }
            }
            set {
                lock (sync)
                {

                    errorCount = value;
                }
            }

        }

        public static long Count
        {
            get {
                lock (sync)
                {
                    return count;
                }
                  
            }
            set {
                lock (sync)
                {
                    count = value;
                }
                  
            }

        }

        private WebClient webClient;

        private void NavigateTo(string Url)
        {
            Download(Url);
        }

        private void Download(string Url)
        {
            lock (sync)
            {
                count++;
            }
            try
            {
                string downBytes = webClient.DownloadString(Url);
                
            }
            catch(Exception ex)
            {
                lock (sync)
                {
                    errorCount++;
                    errorList.Push(new PageError(ex.Message, Url, "N/A"));
                }

            }
        }
        private string ServiceToUrl(string Url, string Service)
        {
            string webUrl = Url.Trim();
            
            if (webUrl.Substring(webUrl.Length - 1) != "/")
            {
                return webUrl + "/_vti_bin/" + Service + ".asmx";
            }
            return webUrl + "_vti_bin/" + Service + ".asmx";

        }

        private SiteDataWS.SiteData dataObj;
        private ListsWS.Lists listObj;

        private static bool connected = false;

        public static bool Connected
        {
            get { return connected; }
        }

        private bool TestUrl(string Url)
        {
            // Save to rollback in case of error
            string localUrl = dataObj.Url;
            dataObj.Url = ServiceToUrl(Url, "SiteData");
            listObj.Url = ServiceToUrl(Url, "Lists");

            try
            {
                dataObj.PreAuthenticate = true;
                string tempUrl = dataObj.Url; // Discover resets the URL
                dataObj.Discover();
                dataObj.Url = tempUrl;
                connected = true;


            }
            catch (Exception ex)
            {
                lock (sync)
                {
                    errorCount++;
                    errorList.Push(new PageError("Unable to connect to web services: "+ex.Message, dataObj.Url, "N/A"));
                }

                dataObj.Url = localUrl;
                listObj.Url = localUrl;

                connected = false;
                return false;
            }
            return true;


        }


        private static Stack<PageError> errorList = new Stack<PageError>();

        public static Stack<PageError> ErrorList
        {
            get { return errorList; }
        }

        public static PageError GetError()
        {
            if (errorList.Count > 0)
            {
                lock (sync)
                {
                    return errorList.Pop();
                }
            }
            return null;
        }
        public void CrawlLists(string baseURL, XmlNodeList ListNodes)
        {
            foreach (XmlNode node in ListNodes)
            {
                if (abort) return;
                XmlDocument doc = new XmlDocument();
                /*
                doc.LoadXml("<Query><OrderBy><FieldRef Name='Sequence'/></OrderBy></Query>");
                XmlNode query = doc.SelectSingleNode(@"Query");
                 */

                NavigateTo(baseURL + node.Attributes["DefaultViewUrl"].Value);
                XmlNode items = null;
                try
                {
                    items = listObj.GetListItems(node.Attributes["ID"].Value, null, null, null, "1000", null, null);
                }
                catch (Exception ex)
                {
                    lock (sync)
                    {
                        count++;
                        errorCount++;
                        errorList.Push(new PageError(ex.Message, listObj.Url, "N/A"));
                    }
                }
                XmlTextReader textReader = new XmlTextReader(items.OuterXml, XmlNodeType.Element,
                    null);
                DataSet ds = new DataSet();
                ds.ReadXml(textReader);

                DataTable sourceTable;
                sourceTable = ds.Tables[ds.Tables.Count - 1];
                foreach (DataRow dr in sourceTable.Rows)
                {
                    if (abort) return;
                    if (sourceTable.Columns.Contains("ows_LinkFilename") || sourceTable.Columns.Contains("ows_ImageSize"))
                    {
                        string fullPath = baseURL + "/" + (dr["ows_FileRef"].ToString()).Split('#')[1];
                        Download(fullPath);
                    } else
                        if (sourceTable.Columns.Contains("ows_FileRef"))
                        {
                            string strId = (dr["ows_FileRef"].ToString()).Split(';')[0];
                            string fullPath = baseURL + "/" + (dr["ows_FileRef"].ToString()).Split('#')[1];
                            string newPath = fullPath.Substring(0,fullPath.LastIndexOf('/')+1)+"DispForm.aspx?ID="+strId;
                            NavigateTo(newPath);

                            
                        }


                }


                //DataTable 
            }
        }

        public void ProcessWeb(string WebUrl)
        {
            bool con = false;
            dataObj.Url = WebUrl;
            if (!connected)
            {
               return;
            }
            else
            {
                con = TestUrl(WebUrl);
            }
            if (con)
            {
                string lastChange = "";
                string nodesStr = "";
                try
                {
                    nodesStr = dataObj.GetContent(SiteDataWS.ObjectType.Site, null, null, null, true, false, ref lastChange);
                }
                catch (Exception ex)
                {
                    lock (sync)
                    {
                        count++;
                        errorCount++;
                        errorList.Push(new PageError(ex.Message, dataObj.Url, "N/A"));
                    }
                }
                if (nodesStr != String.Empty)
                {
                    XmlDocument doc = new XmlDocument();
                    doc.LoadXml(nodesStr);

                    string baseUrl;
                    XmlNode metaUrl = doc.SelectSingleNode(@"Web/Metadata");
                    Uri fullUri = new Uri(metaUrl.Attributes["URL"].Value);

                    baseUrl = fullUri.Scheme+"://"+fullUri.GetComponents(UriComponents.HostAndPort, UriFormat.Unescaped);
                    XmlNodeList nodes;
                    nodes = doc.SelectNodes(@"Web/Lists/List");
                    CrawlLists(baseUrl, nodes);


                    // Recursively call ProcessWeb for all subwebs
                    nodes = doc.SelectNodes(@"Web/Webs/Web");
                    foreach (XmlNode node in nodes)
                    {
                        if (abort) return;
                        this.ProcessWeb(node.Attributes["URL"].Value);
                    }

                }
            }
            //XmlNodeList nodes = 
        }

        public void CrawlWebs(XmlNodeList WebNodes)
        {
            
        }

        public void CrawlFiles()
        {
            throw new System.NotImplementedException();
        }

        public void CrawlFolders()
        {
            throw new System.NotImplementedException();
        }

        public Crawler(SiteDataWS.SiteData SiteData)
        {
            dataObj = new MOSSLoadTest.SiteDataWS.SiteData();
            listObj = new MOSSLoadTest.ListsWS.Lists();
            dataObj.Url = SiteData.Url;
            listObj.Url = SiteData.Url;
            dataObj.Credentials = SiteData.Credentials;
            dataObj.ClientCertificates.Clear();
            dataObj.ClientCertificates.AddRange(SiteData.ClientCertificates);
            listObj.Credentials = SiteData.Credentials;
            listObj.ClientCertificates.Clear();
            listObj.ClientCertificates.AddRange(SiteData.ClientCertificates);
            webClient = new WebClient();
            webClient.Credentials = SiteData.Credentials;
            
            connected = true;
            
            //browser = new WebBrowser();
            
            
        }

         

    }
}
